//
// Created by HP on 2022/1/3.
//

#include "first_app.h"

#include "simple_render_system.h"
#include "lve_camera.h"
#include "keyboard_movement_controller.h"

// std
#include <stdexcept>
#include <array>
#include <chrono>

namespace lve {

    FirstApp::FirstApp() {
        loadGameObjects();
    }

    FirstApp::~FirstApp() {}

    void FirstApp::run() {
        SimpleRenderSystem simpleRenderSystem{lveDevice, lveRenderer.getSwapChainRenderPass()};
        LveCamera camera{};
//        camera.setViewDirection(glm::vec3{0.f}, glm::vec3{0.5f, 0.f, 1.f});
        camera.setViewTarget(glm::vec3{-1.f, -2.f, -0.f}, glm::vec3{0.f, 0.f, 2.5f});

        auto viewObject = LveGameObject::createGameObject();
        KeyboardMovementController cameraController{};

        auto currentTime = std::chrono::high_resolution_clock::now();

        while (!lveWindow.shouldClose()) {
            glfwPollEvents();

            auto newTime = std::chrono::high_resolution_clock::now();
            float frameTime = std::chrono::duration<float, std::chrono::seconds::period>
                    (newTime - currentTime).count();
            currentTime = newTime;
            cameraController.moveInPlaneXZ(lveWindow.getGLFWwindow(), frameTime, viewObject);
            camera.setViewYXZ(viewObject.transform.translation, viewObject.transform.rotation);

            if (glfwGetKey(lveWindow.getGLFWwindow(), GLFW_KEY_L) == GLFW_PRESS) {
                camera.setViewTarget(viewObject.transform.translation, {.0f, .0f, 2.5f});
            }

            float aspect = lveRenderer.getAspectRatio();
//            camera.setOrthographicProjection(-aspect, aspect, -1, 1, -1, 1);
            camera.setPerspectiveProjection(glm::radians(50.f), aspect, 0.1f, 15.f);
            auto commandBuffer = lveRenderer.beginFrame();
            if (commandBuffer != nullptr) {
                lveRenderer.beginSwapChainRenderPass(commandBuffer);
                simpleRenderSystem.renderGameObjects(commandBuffer, gameObjects, camera);
                lveRenderer.endSwapChainRenderPass(commandBuffer);
                lveRenderer.endFrame();
            }
        }
        vkDeviceWaitIdle(lveDevice.device());
    }

    void FirstApp::loadGameObjects() {
        std::shared_ptr<LveModel> lveModel =
                LveModel::createModelFromFile(lveDevice,
                                              "F:/vulkan-tutorial/models/flat_vase.obj");

        auto flatVase = LveGameObject::createGameObject();
        flatVase.model = lveModel;
        flatVase.transform.translation = {-0.5f, .0f, 2.5f};
        flatVase.transform.scale = glm::vec3{3.f, 1.5f, 3.f};
        gameObjects.push_back(std::move(flatVase));

        lveModel =
                LveModel::createModelFromFile(lveDevice,
                                              "F:/vulkan-tutorial/models/smooth_vase.obj");

        auto smoothVase = LveGameObject::createGameObject();
        smoothVase.model = lveModel;
        smoothVase.transform.translation = {.5f, .0f, 2.5f};
        smoothVase.transform.scale = glm::vec3{3.f, 1.5f, 3.f};
        gameObjects.push_back(std::move(smoothVase));
    }
}
